#include "qelib.h"
#include "libfdt.h"
#include "dtb_blob_data.h"

QELOG_DOMAIN("fdt-test")

static int check_compatible(const void *fdt, int nodeoffset, const char *target) {
    const char *compatible;
    int len;

    // 获取 compatible 属性（可能是多个字符串拼接，如 "vendor,device\0vendor2,device2"）
    compatible = fdt_getprop(fdt, nodeoffset, "compatible", &len);
    if (!compatible || len <= 0) {
        return 0; // 无 compatible 属性
    }

    // 遍历所有 compatible 字符串
    while (len > 0) {
        if (strcmp(compatible, target) == 0) {
            return 1; // 匹配成功
        }
        int slen = strlen(compatible) + 1;
        compatible += slen;
        len -= slen;
    }

    return 0; // 未找到匹配
}

static int find_nodes_by_compatible(const void *fdt, int startoffset, const char *target, 
    void (*callback)(const void *fdt, int offset)) 
{
    int offset;

    for (offset = fdt_first_subnode(fdt, startoffset);
         offset >= 0;
         offset = fdt_next_subnode(fdt, offset)) {

        if (check_compatible(fdt, offset, target)) {
            callback(fdt, offset);
        }

        int ret = find_nodes_by_compatible(fdt, offset, target, callback);
        if (ret >= 0) {
            return ret;
        }
    }

    return -1; // 未找到
}

static void axi_uart16550_probe(const void *fdt, int node_offset)
{
    int len;
    int offset;
    const char *name;
    const void *prop;
    const qe_u32 *prop_irq;
    const qe_u32 *prop_irq_parent;

    qe_debug("find axi-uart16550");
    name = fdt_get_name(fdt, node_offset, QE_NULL);
    qe_debug("node name: %s", name);

    prop = fdt_getprop(fdt, node_offset, "clk", &len);
    if (!prop) {
        qe_error("can't find clk property");
        return;
    }
    qe_debug("clk: %d", fdt32_to_cpu(*(qe_u32 *)prop));

    prop = fdt_getprop(fdt, node_offset, "baudrate", &len);
    if (!prop) {
        qe_error("can't find baudrate property");
        return;
    }
    qe_debug("baudrate: %d", fdt32_to_cpu(*(qe_u32 *)prop));
    
    prop_irq_parent = fdt_getprop(fdt, node_offset, "interrupt-parent", &len);
    if (!prop_irq_parent) {
        qe_error("can't find interrupt-parent property");
        return;
    }

    offset = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*prop_irq_parent));
    if (offset < 0) {
        qe_error("can't find interrupt-parent node");
        return;
    }
    name = fdt_get_name(fdt, offset, QE_NULL);
    qe_debug("node name: %s", name);

    prop_irq = fdt_getprop(fdt, node_offset, "interrupts", &len);
    if (!prop) {
        qe_error("can't find interrupts property");
        return;
    }
    qe_debug("interrupts: %d %d %d",
        fdt32_to_cpu(((qe_u32 *)prop)[0]), 
        fdt32_to_cpu(((qe_u32 *)prop)[1]),
        fdt32_to_cpu(((qe_u32 *)prop)[2]));

    prop = fdt_getprop(fdt, node_offset, "trigger", &len);
    if (!prop) {
        qe_error("can't find trigger property");
        return;
    }

    qe_debug("trigger: %d %d", 
        fdt32_to_cpu(((qe_u32 *)prop)[0]), 
        fdt32_to_cpu(((qe_u32 *)prop)[1]));
}

static void earlycon_test(const void *fdt)
{
    int len;
    int offset;
    const char *alias = "serial0";
    const char *path;
    const char *resolved_path;

    int chosen_offset = fdt_path_offset(fdt, "/chosen");
    if (chosen_offset < 0) {
        qe_error("no chosen");
        return;
    }

    const char *stdout_path = fdt_getprop(fdt, chosen_offset, "stdout-path", &len);
    if (!stdout_path) {
        qe_error("no stdout-path");
        return;
    }
    qe_debug("stdout path %s", stdout_path);

    int uart_offset = fdt_path_offset(fdt, stdout_path);
    if (uart_offset < 0) {
        qe_error("no %s", stdout_path);
        return;
    }
    qe_debug("uart offset %d", uart_offset);

    const char *compatible = fdt_getprop(fdt, uart_offset, "compatible", &len);
    qe_debug("compatible %s", compatible);
}

static char heap[10240];

void qe_putc(char c)
{
    putchar(c);
}

int main(int argc, char *argv[])
{
    int ret;
    char data[3];
    
    qemem_set_pool(heap, 10240);

    const void *fdt = dtb_get_blob_data();

    qelog_init(QELOG_DEBUG, QELOG_LV | QELOG_CL);

    qe_hexdump_debug(data, 1);

    ret = fdt_check_header(fdt);
    qe_debug("check header %d", ret);
    if (ret != 0) {
        qe_error("dtb header check failed: %d", ret);
        return 0;
    }

    earlycon_test(fdt);

    find_nodes_by_compatible(fdt, 0, "xlnx,axi-uart16550", axi_uart16550_probe);

    return 0;
}